home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 276-300 / disk_280 / graph / object / f_of_x.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  15KB  |  481 lines

  1. /*
  2.  *                 GRAPH, Version 1.00 - 4 August 1989
  3.  *
  4.  *            Copyright 1989, David Gay. All Rights Reserved.
  5.  *            This software is freely redistrubatable.
  6.  */
  7.  
  8. #include <exec/types.h>
  9. #include <intuition/intuition.h>
  10. #include <graphics/text.h>
  11. #include <math.h>
  12. #include <string.h>
  13.  
  14. #include "object.h"
  15. #include "object/function.h"
  16. #include "object/default.h"
  17. #include "file.h"
  18. #include "graph.h"
  19. #include "uio.h"
  20. #include "coords.h"
  21. #include "list.h"
  22. #include "grph.h"
  23. #include "user/eval.h"
  24. #include "user/gadgets.h"
  25. #include "tracker.h"
  26.  
  27. #include <proto/exec.h>
  28. #include <proto/intuition.h>
  29. #include <proto/graphics.h>
  30.  
  31. /* (private) class f_of_x, inherited from function */
  32. struct f_of_x {
  33.     struct function f;
  34.     char expr[EXPRLEN];       /* the function */
  35.     double oldxmin, oldxmax;  /* limits used at last calculation */
  36.     int waslog;               /* previous x axis type */
  37.     value function, derivee;  /* the compiled function & its differential */
  38. };
  39.  
  40. /*-------------------------------------------------------------------------*/
  41. /*                      f_of_x class implementation                        */
  42. /*-------------------------------------------------------------------------*/
  43.  
  44. /* Return TRUE if f is displayable */
  45. static int f_of_x_ok(const struct f_of_x *this)
  46. {
  47.     return (this->f.min == NOVAL || this->f.max == NOVAL || this->f.min < this-
  48. >f.max) &&
  49.            (this->f.steps == INOVAL || this->f.steps >= 3);
  50. }
  51.  
  52. /* free resources used by this */
  53. static void destroy_f_of_x(struct f_of_x *this)
  54. {
  55.     free_var_list(&this->f.used);
  56.     if (this->f.calc) free_list(&this->f.pts, this->f.sizept);
  57.     this->f.calc = FALSE;
  58.     if (this->function) free_expr(this->function);
  59.     if (this->derivee) free_expr(this->derivee);
  60.     this->function = this->derivee = NULL;
  61. }
  62.  
  63. /* Initialise dependent parts of f_of_x */
  64. static int create_f_of_x(struct f_of_x *this)
  65. {
  66.     this->f.calc = FALSE;
  67.     this->f.var.name = this->f.vname;
  68.     this->function = compile(this->expr);
  69.     if (eval_error != 0)
  70.     {
  71.         message(this->f.o.g, "Compilation error:", eval_messages[eval_error], (
  72. char *)NULL);
  73.         return FALSE;
  74.     }
  75.     this->derivee = differentiate(this->function, this->f.vname);
  76.     if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
  77.     {
  78.         message(this->f.o.g, "Differentiation error:", eval_messages[eval_error
  79. ], (char *)NULL);
  80.         return FALSE;
  81.     }
  82.     if (!make_var_list(this->function, &this->f.used))
  83.         init_var_list(&this->f.used);
  84.     return TRUE;
  85. }
  86.  
  87. /* Allow the user to edit this function (ref: area to refresh) */
  88. static int edit_f_of_x(struct f_of_x *this, struct Region **ref)
  89. {
  90.     struct Requester *req;
  91.     struct Memory *m;
  92.     struct Gadget *gl = NULL, *sd, *nd;
  93.     char from[NBLEN], to[NBLEN], steps[INTLEN], expr[EXPRLEN], xname[VARLEN], c
  94. olour[INTLEN];
  95.     int ret = FALSE;
  96.  
  97.     /* Create requester */
  98.     double2str(from, this->f.min);
  99.     double2str(to, this->f.max);
  100.     int2str(steps, this->f.steps);
  101.     int2str(colour, this->f.colour);
  102.     strcpy(expr, this->expr);
  103.     strcpy(xname, this->f.vname);
  104.  
  105.     *ref = NULL;
  106.     if ((m = NewMemory()) &&
  107.         (req = InitReq(50, 20, 255, 145, m)) &&
  108.         SetReqBorder(req, 1, m) &&
  109.         AddIntuiText(&req->ReqText, "Function", 95, 6, m) &&
  110.         AddText(&gl, 0, "f(", FALSE, xname, VARLEN, TRUE, 0, RELVERIFY, 25, 20,
  111.  25, 10, TRUE, m) &&
  112.         AddText(&gl, 0, ")=", FALSE, expr, EXPRLEN, TRUE, 0, RELVERIFY, 81, 20,
  113.  160, 10, TRUE, m) &&
  114.         AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 49, 40
  115. , 80, 10, TRUE, m) &&
  116.         AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 167, 40, 8
  117. 0, 10, TRUE, m) &&
  118.         AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 57,
  119.  60, 32, 10, TRUE, m) &&
  120.         AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1
  121. 56, 60, 32, 10, TRUE, m) &&
  122.         (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc
  123. * SELECTED, 0, 9, 80, 10, 10, m)) &&
  124.         (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic
  125. edisc * SELECTED, 0, 9, 100, 10, 10, m)) &&
  126.         AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 120, 65, 15, FALS
  127. E, m) &&
  128.         AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 120, 65, 15
  129. , FALSE, m))
  130.     {
  131.         SetReqGadgets(req, gl);
  132.         if (ret = DoRequest(req, this->f.o.g, std_ghandler))
  133.         {
  134.             *ref = full_refresh(this->f.o.g); /* Redraw everything */
  135.             /* Extract typed info */
  136.             this->f.min = str2double(from);
  137.             this->f.max = str2double(to);
  138.             this->f.steps = str2int(steps);
  139.             if ((this->f.colour = str2int(colour)) == INOVAL) this->f.colour =
  140. 1;
  141.             this->f.showdisc = (sd->Flags & SELECTED) != 0;
  142.             this->f.nicedisc = (nd->Flags & SELECTED) != 0;
  143.             strcpy(this->expr, expr);
  144.             strcpy(this->f.vname, xname);
  145.  
  146.             /* Calc new dependent info */
  147.             destroy_f_of_x(this);
  148.             if (this->f.o.ok = f_of_x_ok(this)) this->f.o.ok = create_f_of_x(th
  149. is);
  150.         }
  151.     }
  152.     Free(m);
  153.  
  154.     return ret;
  155. }
  156.  
  157. /* Calculate the points of the function */
  158. static int calc_f_of_x(struct f_of_x *this, int allow_mes)
  159. {
  160.     double x;
  161.     int i;
  162.     struct graph *const g = this->f.o.g;
  163.     /* Use graph limits if none given */
  164.     double const xmin = this->f.min == NOVAL ? g->a.x.min : this->f.min;
  165.     double const xmax = this->f.max == NOVAL ? g->a.x.max : this->f.max;
  166.     int const xlog = g->a.x.log;
  167.     int const steps = (this->f.steps == INOVAL ? DEFSTEPS : this->f.steps) - 1;
  168.      
  169.     double const step = xlog ? pow(xmax / xmin, 1.0 / steps) : (xmax - xmin) /
  170. steps;
  171.     char func[FNAMELEN + 30];
  172.  
  173.     new_list(&this->f.pts);
  174.  
  175.     strcpy(func, "Can't calculate points for ");
  176.     strcat(func, this->f.o.name);
  177.     strcat(func, ":");
  178.  
  179.     if (xmin >= xmax)
  180.     {
  181.         if (allow_mes) message(g, func, "xmin >= xmax", (char *)NULL);
  182.         else alert(g->io.win, "xmin >= xmax", NULL);
  183.         return FALSE;
  184.     }
  185.     if (!create_quick(&this->f.var))
  186.     {
  187.         if (allow_mes) message(g, func, "Couldn't create variable", (char *)NUL
  188. L);
  189.         else alert(g->io.win, func, "Couldn't create variable");
  190.         return FALSE;
  191.     }
  192.  
  193.     /* For all steps x values (evenly spaced *on screen*) */
  194.     for (i = 0, x = xmin; i <= steps; i++, x = xlog ? x * step : x + step)
  195.     {
  196.         point *pt = alloc_node(this->f.sizept);
  197.  
  198.         if (!pt)
  199.         { /* No mem */
  200.             free_list(&this->f.pts, this->f.sizept);
  201.             free_quick(&this->f.var);
  202.             if (allow_mes) message(g, func, "No memory", (char *)NULL);
  203.             return FALSE;
  204.         }
  205.         add_tail(&this->f.pts, pt);
  206.  
  207.         pt->x = x;
  208.         set_quick(&this->f.var, x);
  209.         pt->y = quick_eval(this->function);
  210.         pt->state = (eval_error == 0) ? EXISTS : 0;
  211.     }
  212.     free_quick(&this->f.var);
  213.     return TRUE;
  214. }
  215.  
  216. /* Draw function */
  217. static void draw_f_of_x(struct f_of_x *this, int allow_mes)
  218. {
  219.     struct graph *g = this->f.o.g;
  220.  
  221.     /* If xmax or xmin not specified, track values in graph */
  222.     /* ==> function may need recalculating */
  223.     if (this->f.calc)
  224.         if ((this->f.min == NOVAL && this->oldxmin != g->a.x.min) ||
  225.             (this->f.max == NOVAL && this->oldxmax != g->a.x.max) ||
  226.             this->waslog != g->a.x.log)
  227.         {
  228.             this->f.calc = FALSE;
  229.             free_list(&this->f.pts, this->f.sizept);
  230.         }
  231.  
  232.     if (!this->f.calc) this->f.calc = calc_f_of_x(this, allow_mes);
  233.  
  234.     if (this->f.calc)
  235.     {
  236.         this->oldxmin = g->a.x.min;
  237.         this->oldxmax = g->a.x.max;
  238.         this->waslog = g->a.x.log;
  239.         display_function(&this->f);
  240.     }
  241. }
  242.  
  243. /* Try to improve look of function by adding points. If fails, decides that
  244.    there is a discontinuity */
  245. static struct Region *improve_f_of_x(struct f_of_x *this)
  246. {
  247.     struct graph *const g = this->f.o.g;
  248.     point *pt, *next;
  249.     int ok = FALSE, abort = FALSE, iter;
  250.     /*what y step constitutes a "flat" segment ? Based on window height, could
  251. be better */
  252.     double flat = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height;
  253.     char msg[FNAMELEN + 30];
  254.     char pass[20];
  255.     struct Requester *req;
  256.     struct Region *full = NULL;
  257.  
  258.     /* Flat has no meaning when graph incorrect */
  259.     if (!this->f.o.g->ok) flat = 0.0;
  260.  
  261.     if (!this->f.calc)
  262.     {
  263.         strcpy(msg, this->f.o.name);
  264.         strcpy(msg, "not calculated!");
  265.         message(g, msg, (char *)NULL);
  266.         return NULL;
  267.     }
  268.     if (!this->derivee)
  269.     {
  270.         strcpy(msg, this->f.o.name);
  271.         strcat(msg, " wasn't differentiable");
  272.         message(g, msg, (char *)NULL);
  273.         return NULL;
  274.     }
  275.     if (!create_quick(&this->f.var))
  276.     {
  277.         message(g, "Couldn't create variable", (char *)NULL);
  278.         return NULL;
  279.     }
  280.  
  281.     /* Allow user to abort (can take a very long time !) */
  282.     if (!(req = abort_request(g, "Improve: Pass 1")))
  283.         message(g, "No Memory !", (char *)NULL);
  284.     else
  285.     {
  286.         /* Whole graph will need redrawing */
  287.         full = full_refresh(this->f.o.g);
  288.  
  289.         /* Do MAXITER passes, or until every point ok */
  290.         for (iter = 1; iter <= MAXITER && !ok && !abort; iter++)
  291.         {
  292.             sprintf(pass, "Improve: Pass %d", iter);
  293.             set_abort_msg(req, pass);
  294.             ok = TRUE; /* True as long as no improvements made this pass */
  295.  
  296.             /* Scan all but last point */
  297.             for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next)
  298.             {
  299.                 if (aborted(req)) { abort = TRUE; break; }
  300.  
  301.                 if ((pt->state & (EXISTS | OK)) == EXISTS) /* Only exists. Igno
  302. re points who's segment is "ok" */
  303.                 {
  304.                     double dx;
  305.  
  306.                     pt->state |= OK;
  307.                     pt->state &= ~DISC;
  308.  
  309.                     /* Idea: check if the differential at this point provides
  310.                        a good approximation of the next point. If not, add an
  311.                        extra one.
  312.                        Bad idea: Use the differential at the mid point of the
  313.                        segment. Allows "angles" to remain in the output.
  314.                        Remark: I've tried various other schemes. This one was
  315.                        the best.
  316.                     */
  317.                     set_quick(&this->f.var, pt->x);
  318.                     dx = quick_eval(this->derivee);
  319.                     if (eval_error == 0)
  320.                     {
  321.                         double ecart = next->y - pt->y;
  322.                         /* error: difference between first order taylor approx.
  323.      
  324.                            and actual point. */
  325.                         double error = fabs(ecart - (next->x - pt->x) * dx);
  326.  
  327.                         /* Should we add a point ? error compared with
  328.                            difference (on y axis) between the two points, if
  329.                            nicedisc, small errors (<2 pixels) are accepted
  330.                            without this check */
  331.                         if (error > fabs(ecart) * MAXERROR && (!this->f.nicedis
  332. c || error > flat))
  333.                         {
  334.                             /* Add ONE extra point between the two */
  335.                             pt->state &= ~OK;
  336.                             ok = FALSE; /* We've added a point */
  337.  
  338.                             if (iter == MAXITER) pt->state |= DISC; /* This is
  339. (maybe) a discontinuity */
  340.                             else /* currently ignores BREAKUP(Extension: add mo
  341. re than one point) */
  342.                             {
  343.                                 point *newpt = alloc_node(this->f.sizept);
  344.  
  345.                                 if (!newpt)
  346.                                 {
  347.                                     message(g, "No memory for point !", (char *
  348. )NULL);
  349.                                     abort = TRUE;
  350.                                     break; /* Exit from loop ! */
  351.                                 }
  352.                                 newpt->x = (pt->x + next->x) / 2;
  353.                                 set_quick(&this->f.var, newpt->x);
  354.                                 newpt->y = quick_eval(this->function);
  355.                                 newpt->state = (eval_error == 0) ? EXISTS : 0;
  356.                                 insert(&this->f.pts, newpt, pt);
  357.                             }
  358.                         }
  359.                     }
  360.                 }
  361.             }
  362.         }
  363.         end_abort_request(req);
  364.     }
  365.     free_quick(&this->f.var);
  366.     return full;
  367. }
  368.  
  369. /* Provide quick textual form of function */
  370. static char *f2str_f_of_x(struct f_of_x *this, char *buf, int maxlen)
  371. {
  372.     buf[maxlen - 1] = '\0';
  373.     strncpy(buf, this->f.o.name, maxlen - 1);
  374.     strncat(buf, "(", maxlen - strlen(buf) - 1);
  375.     strncat(buf, this->f.vname, maxlen - strlen(buf) - 1);
  376.     strncat(buf, ")=", maxlen - strlen(buf) - 1);
  377.     strncat(buf, this->expr, maxlen - strlen(buf) - 1);
  378.  
  379.     return buf;
  380. }
  381.  
  382. /* Did user select us ? */
  383. static int down_f_of_x(struct f_of_x *this)
  384. {
  385.     struct graph *g = this->f.o.g;
  386.  
  387.     if (this->f.o.ok && this->f.calc) /* visible ? */
  388.     {
  389.         int inside;
  390.  
  391.         if (!create_quick(&this->f.var))
  392.         {
  393.             message(g, "Couldn't create variable", (char *)NULL);
  394.             return FALSE;
  395.         }
  396.  
  397.         set_quick(&this->f.var, g->s.x);
  398.         /* Calculate y = f(x click pos) and compare with y click pos */
  399.         inside = fabs(g->io.rw->sy(g->io.rw, quick_eval(this->function)) - g->i
  400. o.rw->sy(g->io.rw, g->s.y)) < FDIST &&
  401.                  eval_error == 0;
  402.         free_quick(&this->f.var);
  403.  
  404.         return inside;
  405.     }
  406.     return FALSE;
  407. }
  408.  
  409. /* Write f_of_x specific info */
  410. static int save_f_of_x(struct f_of_x *this, FILE *f)
  411. {
  412.     short tag = F_OF_X_TAG;
  413.     short end = F_OF_X_END;
  414.  
  415.     return WRITE(f, tag) &&
  416.            WRITE(f, this->expr) &&
  417.            WRITE(f, end);
  418. }
  419.  
  420. /* Delete a member of class f_of_x */
  421. static struct Region *delete_f_of_x(struct f_of_x *this)
  422. {
  423.     struct Region *full = full_refresh(this->f.o.g);
  424.  
  425.     destroy_f_of_x(this);
  426.     FreeMem(this, sizeof(struct f_of_x));
  427.     return full;
  428. }
  429.  
  430. /* Create a new instance of f_of_x */
  431. struct f_of_x *new_f_of_x(struct graph *g, char *name)
  432. {
  433.     struct f_of_x *this = AllocMem(sizeof(struct f_of_x), MEMF_CLEAR);
  434.  
  435.     if (this)
  436.     {
  437.         /* Standard init */
  438.         init_function(&this->f, g, name);
  439.         /* Setup methods */
  440.         this->f.save = (void *)save_f_of_x;
  441.         this->f.o.delete = (void *)delete_f_of_x;
  442.         this->f.o.down = (void *)down_f_of_x;
  443.         this->f.o.draw = (void *)draw_f_of_x;
  444.         this->f.o.edit = (void *)edit_f_of_x;
  445.         this->f.o.improve = (void *)improve_f_of_x;
  446.         this->f.o.f2str = (void *)f2str_f_of_x;
  447.         this->f.sizept = sizeof(point);
  448.         return this;
  449.     }
  450.     message(g, "Couldn't create function:", "No memory", (char *)NULL);
  451.     return NULL;
  452. }
  453.  
  454. /* Load f_of_x from a file */
  455. struct f_of_x *load_f_of_x(struct graph *g, FILE *f)
  456. {
  457.     struct f_of_x *this = new_f_of_x(g, "");
  458.  
  459.     if (this)
  460.     {
  461.         short end;
  462.  
  463.         if (READ(f, this->expr) &&
  464.             READ(f, end) &&
  465.             end == F_OF_X_END)
  466.         {
  467.             /* Load standard part */
  468.             load_rest(&this->f, f);
  469.             if (this->f.o.ok = f_of_x_ok(this)) this->f.o.ok = create_f_of_x(th
  470. is);
  471.  
  472.             return this;
  473.         }
  474.         delete_f_of_x(this);
  475.     }
  476.     return NULL;
  477. }
  478.  
  479.  
  480.  
  481.